// 装饰器
// 一、类装饰器
// 1、基本语法：类庄思琪是一个应用在类声明上的函数，可以为类添加额外的功能，或添加额外的逻辑

// 装饰器函数
function DemoPerson(target: Function) {
    console.log('我是装饰器函数', target);
}
// 使用装饰器
@DemoPerson   //这句代码其实就是等价于： DemoPerson(PersonDecoration)
class PersonDecoration {
    constructor(
        public name: string,
        public age: number
    ) {

    }
}

// DemoPerson 函数会在PersonDecoration类定义时执行
// 参数说明：target 参数是被装饰的类，即：PersonDecoration
// 重点注释：使用装饰器必须在class类的上面一行

// 2、案例：调用demotest1的toString方法打印的是JSON字符串
@CustomString
class Demo1 {
    constructor(
        public name: string,
        public age: number
    ) { }
}
const demotest1 = new Demo1('张三', 18)
// console.log('这是原本的toString',demotest1.toString());    //不使用装饰器打印的结果却是："[Object,Object]"
console.log('这是有装饰器的toString', demotest1.toString());    //使用装饰器打印的结果是："{"name":"张三","age":18}"
function CustomString(target: Function) {
    target.prototype.toString = function () {
        return JSON.stringify(this)  //这里JSON.stringify()里的参数得是this，而不是target，因为target是类，不是实例变量
    }
    Object.seal(target) //Object.seal()封锁，对prototype进行封锁，不允许随意对prototype进行扩展
}

// 3、关于返回值：
//   类装饰器有返回值：若类装饰器返回一个新的类，那这个新类将替换掉被装饰的类
//   类装饰器无返回值: 若类装饰器无返回值或返回undefined，那么装饰器的类不会被替换

function xiha(target: Function) {
    return class {
        test() {
            console.log(100);
            console.log(200);
            console.log(300);

        }
    }
}

@xiha
class xihatest {
    test() {
        console.log(111);
    }
}
console.log('xihatest', xihatest);


//4、关于构造类型
// 在TS中，Function类型所表示的范围十分广泛，包括：普通函数、箭头函数、方法等等
// 但并非Function类型的函数都可以被new关键字实例化，例如箭头函数是不能被实例化的，
// 那么TS中该如何什么一个构造类型呢？有以下两种方法 :

// (1)、仅申明构造类型
type selfType = new (...agrs: any[]) => {}
// new：表示该类型可以被new操作符调用    ...agrs：构造器可以接受多个参数
// any[]：表示构造器可以接受任意类型的参数  {}:表示返回类型是对象(非null、非undefined的对象)

// 需求fn得是一个类
function testFn(fn:selfType){}

const personAxios = ()=>{}
// testFn(personAxios)   //报错
class personClass{}
testFn(personClass)

// (2)、申明构造类型 + 指定静态属性
function WifeTest(fn:staticPerson){}
type staticPerson = {
    new (...agrs: any[]):{}
    wife:string
}
class wifePerson{
    static wife:string   //因为staticPerson是包含静态属性的，所以wife必须要static修饰符
}
WifeTest(wifePerson)

// 5、替换被装饰的类
// 对于高级一些的装饰器，不仅仅是覆盖一个原型上的方法，还要有更多功能，例如添加新的方法和状态

// 需求：设计一个LOgtime装饰器，可以给市里添加一个属性，用于记录实例对象的创建时间，再添加一个方法用于读取创建时间
type tmieType = new(...agrs:any[])=>{}
function LogTime<T extends tmieType>(target:T){
    //T extends tmieType 这里的extends不是继承，而是对泛型T的一个变量限制，使T只能是符合timeType定义的对象类型 
    return class extends target {
        createTime:Date
        constructor(...args:any[]){
            super(...args)
            this.createTime = new Date()
        }
        getTime(){
            console.log('该对象的创建时间是:'+this.createTime);
        }
    }
}
@LogTime
class ximyPerson {
    constructor(
        public name:string,
        public age:number
    ){}
    speak(){
        console.log('你好啊');
    }
}
// 必须要有ximyperson这个接口，不然xmyp1.getTime()会报错，
interface ximyPerson{
    getTime():void
    // 利用接口的可合并性，定义一个getTime方法
}
const xmyp1 = new ximyPerson('张三',18)

xmyp1.getTime()